package server

import (
	"database/sql/driver"
	"encoding/json"
	"io/ioutil"
	"math/rand"
	"net/http"
	"reflect"
	"strconv"
	"time"

	"git.oschina.net/dddailing/drsys/backend/model"
	"github.com/dailing/levlog"
)

type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (nt *NullTime) Scan(value interface{}) error {
	nt.Time, nt.Valid = value.(time.Time)
	return nil
}

// Value implements the driver Valuer interface.
func (nt NullTime) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	}
	return nt.Time, nil
}

type JsonType map[string]interface{}

func NewJson() *JsonType {
	retval := make(JsonType)
	return &retval
}
func NewJsonFromString(s string) *JsonType {
	retval := make(JsonType)
	err := json.Unmarshal([]byte(s), &retval)
	levlog.E(err)
	return &retval
}

func (j *JsonType) GetString(key string) string {
	if f, ok := (*j)[key].(string); ok {
		return f
	}
	if f, ok := (*j)[key].([]byte); ok {
		return string(f)
	}
	levlog.Error("Error convert ", key)
	return ""
}

func (j *JsonType) GetArrayOfInterface(key string) []interface{} {
	if f, ok := (*j)[key].([]interface{}); ok {
		return f
	}
	levlog.Error("Error convert ", key, "Objtype:", reflect.TypeOf((*j)[key]))
	return nil
}

func (j *JsonType) GetArrayOfInt(key string) []float64 {
	objarr := j.GetArrayOfInterface(key)
	result := make([]float64, 0)
	for i := 0; i < len(objarr); i++ {
		if ele, ok := objarr[i].(float64); ok {
			result = append(result, ele)
		} else {
			levlog.Error("Error converting ", i, "th element, Which is:", objarr[i],
				" Type is:", reflect.TypeOf(objarr[i]))
			return nil
		}
	}
	return result
}

func (j *JsonType) GetArrayOfObject(key string) []*JsonType {
	objarr := j.GetArrayOfInterface(key)
	result := make([]*JsonType, 0)
	for i := 0; i < len(objarr); i++ {
		if ele, ok := objarr[i].(map[string]interface{}); ok {
			var jsonTypes JsonType = ele
			result = append(result, &jsonTypes)
		} else {
			levlog.Error("Error converting ", i, "th ele, Which is:", objarr[i],
				" Type is:", reflect.TypeOf(objarr[i]))
			return nil
		}
	}
	return result
}

func (j *JsonType) GetBytes(key string) []byte {
	if f, ok := (*j)[key].([]byte); ok {
		return f
	}
	if f, ok := (*j)[key].(string); ok {
		return []byte(f)
	}
	levlog.Error("Error convert :", key)
	return nil
}

func (j *JsonType) GetJson(key string) *JsonType {
	if f, ok := (*j)[key].(*JsonType); ok {
		return f
	}
	levlog.Warning("Not JsonType,", (*j)[key])
	payload, err := json.Marshal((*j)[key])
	levlog.E(err)
	return NewJsonFromString(string(payload))
}

func (j *JsonType) GetObj(key string) interface{} {
	return (*j)[key]
}

func (j *JsonType) GetInt(key string) int {
	val, ok := (*j)[key]
	if !ok {
		return 0
	}
	if s, ok := val.(string); ok {
		i, err := strconv.Atoi(s)
		levlog.E(err)
		return i
	}
	if i, ok := val.(int); ok {
		return i
	}
	if i, ok := val.(int64); ok {
		return int(i)
	}
	if i, ok := val.(float64); ok {
		return int(i)
	}
	if i, ok := val.(float32); ok {
		return int(i)
	}
	levlog.Error("Get Key Error ", reflect.TypeOf(val))
	return 0
}

func (j *JsonType) GetFloat(key string) float64 {
	val, ok := (*j)[key]
	if !ok {
		return 0
	}
	if s, ok := val.(string); ok {
		i, err := strconv.ParseFloat(s, 64)
		levlog.E(err)
		return i
	}
	if i, ok := val.(float64); ok {
		return i
	}
	if i, ok := val.(float32); ok {
		return float64(i)
	}
	return float64(j.GetInt(key))
}

const (
	RequestKeyBody      = "__req_body__"
	RequestKeyObj       = "__req_obj__"
	RequestKeyJson      = "__req_json__"
	RequestKeyToken     = "__req_token__"
	RequestKeyHeader    = "__req_header__"
	RequestKeyRawReqObj = "__req_raw_req__"
	ResponseKeyHeader   = "__resp_header__"
	ResponseKeyRawDara  = "__payload__"
)

func (j *JsonType) Set(key string, val interface{}) {
	(*j)[key] = val
}

// These function is server related
func (j *JsonType) GetReqBody() []byte {
	return j.GetBytes(RequestKeyBody)
}

func (j *JsonType) GetReqObj() interface{} {
	return j.GetObj(RequestKeyObj)
}

func (j *JsonType) SetReqObj(i interface{}) {
	j.Set(RequestKeyObj, i)
}

func (j *JsonType) GetReqJson() *JsonType {
	return j.GetJson(RequestKeyJson)
}

func (j *JsonType) GetReqToken() *model.UserToken {
	token := j.GetObj(RequestKeyToken).(*model.UserToken)
	return token
}

func (j *JsonType) GetReqHeader() http.Header {
	return j.GetObj(RequestKeyHeader).(http.Header)
}

func (j *JsonType) GetRespWriter() *http.ResponseWriter {
	return j.GetObj(ResponseKeyHeader).(*http.ResponseWriter)
}

func (j *JsonType) GetReqRaw() *http.Request {
	return j.GetObj(RequestKeyRawReqObj).(*http.Request)
}

func (j *JsonType) Remove(key string) {
	delete(*j, key)
}

func (j *JsonType) String() string {
	ss, err := json.Marshal(j)
	levlog.E(err)
	return string(ss)
}

func JsonReq(f func(w http.ResponseWriter, r *http.Request) *JsonType) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		retval := f(w, r)
		retv, err := json.Marshal(retval)
		levlog.E(err)
		_, err = w.Write(retv)
		levlog.E(err)
	}
}

////////////////////////////////////////////////////////////

type JsonRespReqFunc func(*JsonType) (*JsonType, int)

const (
	RESPONSE_TYPE_JSON = iota
	RESPONSE_TYPE_ERROR
	RESPONSE_TYPE_RAW
	RESPONSE_TYPE_MIDWARE
)

func HandleJsonReq(funcList ...JsonRespReqFunc) http.HandlerFunc {
	var err error
	return func(w http.ResponseWriter, r *http.Request) {
		j := NewJson()
		levlog.Trace("vvvvvvvvvvvvvvvvvvvvvREQvvvvvvvvvvvvvvvvvvvvvv")
		levlog.Trace("HOST:", r.Host)
		levlog.Trace("URL:", r.URL.Path)
		levlog.Trace("METHOD:", r.Method)
		levlog.Trace("----------------------------------------------")
		j.Set(RequestKeyToken, r.Header.Get("token"))
		j.Set(RequestKeyHeader, r.Header)
		j.Set(RequestKeyRawReqObj, r)
		j.Set(ResponseKeyHeader, &w)
		j.Set("__req_url__", r.URL.Path)
		var lastRet *JsonType
		var succ int
		for _, f := range funcList {
			lastRet, succ = f(j)
			if succ == RESPONSE_TYPE_MIDWARE {
				continue
			} else if succ == RESPONSE_TYPE_ERROR {
				w.WriteHeader(lastRet.GetInt("code"))
				_, err := w.Write(lastRet.GetBytes("info"))
				levlog.E(err)
				return
			}
		}
		var payload []byte
		w.Header()
		if succ == RESPONSE_TYPE_RAW {
			payload = lastRet.GetBytes(ResponseKeyRawDara)
		} else if succ == RESPONSE_TYPE_JSON {
			payload, err = json.Marshal(lastRet)
			levlog.E(err)
			if err != nil {
				w.WriteHeader(500)
				_, err := w.Write([]byte(err.Error()))
				levlog.E(err)
			}
		}
		w.WriteHeader(200)
		_, err := w.Write(payload)
		levlog.E(err)
		levlog.Trace("^^^^^^^^^^^^^^^^^^^^^END^^^^^^^^^^^^^^^^^^^^^^")
	}
}

func ReadBody(jsonType *JsonType) (*JsonType, int) {
	r := jsonType.GetReqRaw()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		levlog.E(err)
		ResponseError(500, err.Error())
	}
	if len(body) < 2048 {
		levlog.Trace("REQ BODY:", string(body))
	}
	jsonType.Set(RequestKeyBody, string(body))
	return NoResponse()
}

func BodyToJson(jsonType *JsonType) (*JsonType, int) {
	j := NewJson()
	err := json.Unmarshal(jsonType.GetBytes(RequestKeyBody), j)
	if err != nil {
		levlog.E(err)
		return ResponseError(403, "Request Data Error")
	}
	jsonType.Set(RequestKeyJson, j)
	return NoResponse()
}

func BodyToObj(obj interface{}) func(jsonType *JsonType) (*JsonType, int) {
	return func(jsonType *JsonType) (*JsonType, int) {
		j := reflect.New(reflect.TypeOf(obj).Elem()).Interface()
		err := json.Unmarshal(jsonType.GetBytes(RequestKeyBody), j)
		if err != nil {
			levlog.E(err)
			return ResponseError(403, "Request Data Error")
		}
		jsonType.Set(RequestKeyObj, j)
		return NoResponse()
	}
}

/*
 If every previous steps is good, return 200 ok with nothing, else return error
*/
func ResponseError(code int, info string) (*JsonType, int) {
	j := NewJson()
	j.Set("code", code)
	j.Set("info", info)
	return j, RESPONSE_TYPE_ERROR
}

func ResponseJson(j *JsonType) (*JsonType, int) {
	return j, RESPONSE_TYPE_JSON
}

func ResponseAnyToJson(any interface{}) (*JsonType, int) {
	payload, err := json.Marshal(any)
	levlog.E(err)
	return ResponseRaw(string(payload))
}

func ResponseRaw(s string) (*JsonType, int) {
	j := NewJson()
	j.Set(ResponseKeyRawDara, s)
	return j, RESPONSE_TYPE_RAW
}
func ResponseSucc() (*JsonType, int) {
	return ResponseRaw("succ")
}
func NoResponse() (*JsonType, int) {
	return nil, RESPONSE_TYPE_MIDWARE
}

var seededRand = rand.New(
	rand.NewSource(time.Now().UnixNano()))

func randStringGen(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyz" +
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}
